package com.qys.livesMall.financial.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qys.livesMall.app.user.model.entity.H2AppUser;
import com.qys.livesMall.app.user.model.entity.H2AppUserBalance;
import com.qys.livesMall.app.user.model.entity.H2AppUserRechargeWithdrawals;
import com.qys.livesMall.app.user.model.entity.UserRewardConfig;
import com.qys.livesMall.app.user.service.IH2AppUserBalanceService;
import com.qys.livesMall.app.user.service.IH2AppUserRechargeWithdrawalsService;
import com.qys.livesMall.app.user.service.IH2AppUserService;
import com.qys.livesMall.app.user.service.UserRewardConfigService;
import com.qys.livesMall.common.exception.QysException;
import com.qys.livesMall.common.result.R;
import com.qys.livesMall.common.utils.DateUtils;
import com.qys.livesMall.common.utils.MessageUtils;
import com.qys.livesMall.finance.enums.AccountBusinessTypeEnum;
import com.qys.livesMall.finance.model.dto.FinancePayDTO;
import com.qys.livesMall.finance.service.FinanceBusinessService;
import com.qys.livesMall.finance.util.BusinessNoUtil;
import com.qys.livesMall.financial.event.OfflineFinishEvent;
import com.qys.livesMall.financial.mapper.FinancialOfflineMapper;
import com.qys.livesMall.financial.mapper.FinancialOfflineUserMapper;
import com.qys.livesMall.financial.model.dto.app.AppFinancialOfflineUserDTO;
import com.qys.livesMall.financial.model.dto.app.OfflineFinishRewardDTO;
import com.qys.livesMall.financial.model.dto.sys.FinancialOfflineUserPageDTO;
import com.qys.livesMall.financial.model.entity.FinancialOffline;
import com.qys.livesMall.financial.model.entity.FinancialOfflineUser;
import com.qys.livesMall.financial.model.entity.FinancialOfflineUserDetail;
import com.qys.livesMall.financial.model.vo.sys.FinancialOfflineUserVO;
import com.qys.livesMall.financial.service.FinancialOfflineUserDetailService;
import com.qys.livesMall.financial.service.FinancialOfflineUserService;
import com.qys.livesMall.framework.utils.SecurityUtils;
import com.qys.livesMall.system.model.vo.sys.H2SysShareConfigVO;
import com.qys.livesMall.system.service.SysShareConfigService;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotNull;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 用户完成线下理财信息 服务实现类
 * </p>
 *
 * @author
 * @since 2023-06-10
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class FinancialOfflineUserServiceImpl extends ServiceImpl<FinancialOfflineUserMapper, FinancialOfflineUser> implements FinancialOfflineUserService {

    private final IH2AppUserService userService;
    private final FinancialOfflineMapper offlineMapper;
    private final ApplicationContext applicationContext;
    private final SysShareConfigService shareConfigService;
    private final IH2AppUserBalanceService userBalanceService;
    private final FinancialOfflineUserDetailService financialOfflineUserDetailService;
    private final FinanceBusinessService financeBusinessService;
    @Resource
    private  IH2AppUserRechargeWithdrawalsService userRechargeService;
    @Resource
    private UserRewardConfigService userRewardConfigService;
    /**
     * 用户完成任务
     * @param dto
     */
    @Override
    @Transactional
    public void finishTask(AppFinancialOfflineUserDTO dto) {
        log.info("用户完成线下理财信息:{},理财id{},任务序号:{}", SecurityUtils.getUsername(), dto.getOfflineId(), dto.getNumber());

        FinancialOffline offline = offlineMapper.selectById(dto.getOfflineId());
        if (null == offline) {
            throw new QysException(MessageUtils.message("financial.null"));
        }

        //校验用户任务信息
        FinancialOfflineUser offlineUser = checkUserFinancialInfo(offline, dto.getNumber(),dto.getVideoIds());

        //用户任务完成情况
        if (Objects.equals(offlineUser.getFinishCount(), offlineUser.getFinancialCount())) {

            //上级分佣
            OfflineFinishRewardDTO rewardDTO = new OfflineFinishRewardDTO();
            rewardDTO.setReward(offline.getIncome());
            rewardDTO.setUserId(SecurityUtils.getUserId());
            rewardDTO.setOrderNo(offlineUser.getOrderNo());
            BigDecimal superiorIncome = addUserParentBalance(rewardDTO);

            //用户收益（用户收益 = 总收益 - 上级总分佣）
            BigDecimal reward = addUserBalance(offline.getIncome().subtract(superiorIncome));

            //已完成
            offlineUser.setFinishTime(LocalDateTime.now());
            offlineUser.setNextFinancialTime(LocalDate.now().plusDays(offline.getCycle()));
            offlineUser.setReward(reward);
        }

        //更新用户任务表
        baseMapper.update(offlineUser, Wrappers.<FinancialOfflineUser>query()
                .eq("user_id", SecurityUtils.getUserId())
                .eq("financial_id", dto.getOfflineId())
                .isNull("finish_time")
        );
    }

    /**
     * 用户理财完成校验
     *
     * @param offline  {@link FinancialOffline}
     * @param number   完成序号
     * @param videoIds
     */
    private FinancialOfflineUser checkUserFinancialInfo(FinancialOffline offline, Byte number,List<Long> videoIds) {
        FinancialOfflineUser offlineUser = baseMapper.selectOne(Wrappers.<FinancialOfflineUser>query()
                .eq("user_id", SecurityUtils.getUserId())
                .eq("financial_id", offline.getId())
                .orderBy(true, false, "finish_time is null", "finish_time")
                .last("LIMIT 1")
        );

        //任务正在进行中
        if (null != offlineUser && offlineUser.getFinishTime() == null) {
            //获取用户看过视频序号
            List<Byte> detailSort = JSONUtil.toList(offlineUser.getFinancialSort(), Byte.class);
            //已经看过
            if (detailSort.contains(number)) {
                throw new QysException(MessageUtils.message("financial.repeat.finish"));
            }

            //设置任务完成信息
            detailSort.add(number);
            Collections.sort(detailSort);
            offlineUser.setFinancialSort(JSONUtil.toJsonStr(detailSort));

            //用户完成数量
            offlineUser.setFinishCount((byte) (offlineUser.getFinishCount() + 1));
        }
        //未进行 或 已经完成
        else {
            //完成过了但是冷却时间还没有到
            if (null != offlineUser && offlineUser.getNextFinancialTime().isAfter(LocalDate.now())) {
                throw new QysException(MessageUtils.message("financial.repeat.finish"));
            }

            offlineUser = new FinancialOfflineUser();
            offlineUser.setFinancialId(offline.getId());
            offlineUser.setUserId(SecurityUtils.getUserId());
            offlineUser.setFinancialCount(offline.getDetailCount());
            offlineUser.setFinishCount((byte) 1);
            offlineUser.setFinancialSort(JSONUtil.toJsonStr(Collections.singletonList(number)));
            offlineUser.setOrderNo(BusinessNoUtil.get(AccountBusinessTypeEnum.FINANCIAL_OFFLINE_USER_GET));
            baseMapper.insert(offlineUser);

            if (CollectionUtils.isNotEmpty(videoIds)){
                List<FinancialOfflineUserDetail> details=new ArrayList<>();
                for (Long id :videoIds){
                    FinancialOfflineUserDetail detail=new FinancialOfflineUserDetail();
                    detail.setUserId(SecurityUtils.getUserId());
                    detail.setUserName(SecurityUtils.getUsername());
                    detail.setState(1);
                    detail.setCreateTime(DateUtils.dateTimeNow());
                    detail.setVideoId(id);
                    detail.setTaskState(1);
                    detail.setFinancialId(offlineUser.getFinancialId());
                    detail.setOfflineUserId(offlineUser.getId());
                    details.add(detail);
                }
                financialOfflineUserDetailService.saveBatch(details);
            }
        }

        return offlineUser;
    }

    /**
     * 添加用户收益
     *
     * @param userRevenue 用户应得收益
     */
    private BigDecimal addUserBalance(BigDecimal userRevenue) {
        //查询用户余额
        H2AppUserBalance userBalance = userBalanceService.queryByUserId(SecurityUtils.getUserId());
        if (null == userBalance) {
            throw new QysException(MessageUtils.message("financial.balance.not.match"));
        }

/*
        //计算奖励的金额
        BigDecimal amount = userBalance.getBalance().compareTo(offline.getAmountMax()) > 0 ? offline.getAmountMax() :
                userBalance.getBalance();

        //计算奖励
        BigDecimal reward = amount.multiply(offline.getIncome());
*/

        //添加用户余额
        FinancePayDTO payDTO = new FinancePayDTO();
        payDTO.setUserId(SecurityUtils.getUserId());
        payDTO.setBusinessNo(BusinessNoUtil.get(AccountBusinessTypeEnum.FINANCIAL_OFFLINE_USER_GET));
        payDTO.setAmountIncurred(userRevenue);
        payDTO.setBusinessType(AccountBusinessTypeEnum.FINANCIAL_OFFLINE_USER_GET);
        financeBusinessService.pay(payDTO);

        return userRevenue;
    }

    /**
     * 理财任务完成上级用户分佣
     *
     * @param dto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BigDecimal addUserParentBalance(OfflineFinishRewardDTO dto) {
        log.info("addUserParentBalance dto:{}", JSONObject.toJSONString(dto));
        Map<String, BigDecimal> parentIncome = new HashMap<>();

        H2AppUser appUser = userService.getById(dto.getUserId());
        if (null == appUser) {
            log.error("【线下理财未获取到用户信息,不能给上级分佣:{}】", dto.getUserId());
            return BigDecimal.ZERO;
        }

        List<H2SysShareConfigVO> configVOS = shareConfigService.list((byte) 1);
        if (CollectionUtils.isEmpty(configVOS)) {
            log.error("【线下理财,还未配置上级分红信息,不能给上级分佣】");
            return BigDecimal.ZERO;
        }

        int index = 0;
        while (appUser.getSuperior() != null && index < configVOS.size()) {
            appUser = userService.getById(appUser.getSuperior());
            if (null == appUser) {
                log.error("【线下理财未获取到用户信息,不能给上级分佣:{}】", dto.getUserId());
                return BigDecimal.ZERO;
            }

            //分红比例
            H2SysShareConfigVO configVO = configVOS.get(index);
            log.info("【当前层级分佣比例为：{}】", configVO.getDividendRatio());
            BigDecimal dividendRatio = configVO.getDividendRatio();

            //计算奖励
            BigDecimal reward = dto.getReward().multiply(dividendRatio).setScale(4, RoundingMode.DOWN);
            parentIncome.put(appUser.getId(), reward);

            index++;
        }

        parentIncome.forEach((userId, reward) -> {
            FinancePayDTO payDTO = new FinancePayDTO();
            payDTO.setUserId(Long.valueOf(userId));
            payDTO.setBusinessNo(dto.getOrderNo());
            payDTO.setAmountIncurred(reward);
            payDTO.setBusinessType(AccountBusinessTypeEnum.FINANCIAL_OFFLINE_USER_SUPERIOR_GET);
            financeBusinessService.pay(payDTO);
        });

        BigDecimal superiorCommission = parentIncome.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        log.info("上级分佣总和:{}", superiorCommission);
        return superiorCommission;
    }

    @Override
    public IPage<FinancialOfflineUserVO> userPage(FinancialOfflineUserPageDTO pageDTO) {
        LambdaQueryWrapper<FinancialOfflineUser> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(null!=pageDTO.getFinishCount(), FinancialOfflineUser::getFinishCount,pageDTO.getFinishCount());
        queryWrapper.like(StringUtil.isNotBlank(pageDTO.getUserName()),FinancialOfflineUser::getUserName,pageDTO.getUserName());
        queryWrapper.orderByAsc(FinancialOfflineUser::getFinishTime);
        IPage<FinancialOfflineUserVO> page=page(new Page<>(pageDTO.getPageNum(), pageDTO.getPageSize()), queryWrapper).convert(e -> {
            FinancialOfflineUserVO financialOfflineUserVO=new FinancialOfflineUserVO();
            BeanUtils.copyProperties(e,financialOfflineUserVO);
            return financialOfflineUserVO;
        });
        return page;
    }

    @Override
    public R<?> withdrawalCheck(Long userId) {
        LambdaQueryWrapper<H2AppUserRechargeWithdrawals> query=new LambdaQueryWrapper<>();
        query.eq(H2AppUserRechargeWithdrawals::getUserId,userId);
        query.eq(H2AppUserRechargeWithdrawals::getType,AccountBusinessTypeEnum.WITHDRAWAL.getCode());
        List<H2AppUserRechargeWithdrawals> list=userRechargeService.list(query);
        if (CollectionUtils.isNotEmpty(list)){
            return R.ok();
        }
        LambdaQueryWrapper<UserRewardConfig> userRewardConfigQuery=new LambdaQueryWrapper<>();
        userRewardConfigQuery.eq(UserRewardConfig::getAwardType,1);
        userRewardConfigQuery.last("limit 1");
        UserRewardConfig rewardConfigs=userRewardConfigService.getOne(userRewardConfigQuery);
        if (null == rewardConfigs || null == rewardConfigs.getAwardUnit()){
            return R.ok();
        }
        LambdaQueryWrapper<FinancialOfflineUser> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(FinancialOfflineUser::getUserId,userId);
        queryWrapper.isNotNull(FinancialOfflineUser::getFinishTime);
        Long financialOfflineUsers=count(queryWrapper);
        if (rewardConfigs.getAwardUnit().compareTo(new BigDecimal(financialOfflineUsers)) > 0){
            return R.fail(rewardConfigs.getAwardUnit().intValue());
        }
        return R.ok();
    }

    @Override
    public List<FinancialOfflineUser> getYesterday() {
        return baseMapper.getYesterday();
    }
}
